/**
 * Author : Jorge Mejuto Create : 18/05/2010
 * 
 * 
 * 
 */

package net.eyra.utiles;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;

public class Periodo {

    public final static int FECHA_INICIAL = 1;
    public final static int FECHA_FINAL = 2;

    public final static int MEDIDA_TM1 = 1;
    public final static int MEDIDA_TM2 = 2;

    public final static int FORMATO_FECHA_AAAA_MM_DD_HH_MM = 1;
    public final static int FORMATO_FECHA_DD_MM_AAAA_HH_MM = 2;
    public final static int FORMATO_FECHA_AAAA_MM_DD = 3;
    public final static int FORMATO_FECHA_DD_MM_AAAA = 4;

    public final static int SEPARADOR_FECHA_SLASH = 1;
    public final static int SEPARADOR_FECHA_GUION = 2;

    public final static int DIA = Calendar.DAY_OF_MONTH;
    public final static int MES = Calendar.MONTH;
    public final static int AÑO = Calendar.YEAR;
    public final static int HORA = Calendar.HOUR_OF_DAY;
    public final static int MINUTO = Calendar.MINUTE;
    public final static int SEGUNDO = Calendar.SECOND;
    public final static int MILISEGUNDO = Calendar.MILLISECOND;
    public final static int DIAS_MES = 100;
    public final static int DIAS_AÑO = 101;

    public final static int ENERO = Calendar.JANUARY;
    public final static int FEBRERO = Calendar.FEBRUARY;
    public final static int MARZO = Calendar.MARCH;
    public final static int ABRIL = Calendar.APRIL;
    public final static int MAYO = Calendar.MAY;
    public final static int JUNIO = Calendar.JUNE;
    public final static int JULIO = Calendar.JULY;
    public final static int AGOSTO = Calendar.AUGUST;
    public final static int SEPTIEMBRE = Calendar.SEPTEMBER;
    public final static int OCTUBRE = Calendar.OCTOBER;
    public final static int NOVIEMBRE = Calendar.NOVEMBER;
    public final static int DICIEMBRE = Calendar.DECEMBER;

    public final static int TIPO_INICIALIZACION_DIA = 1;
    public final static int TIPO_INICIALIZACION_MES = 2;
    public final static int TIPO_INICIALIZACION_AÑO = 3;

    private GregorianCalendar calendario = (GregorianCalendar) GregorianCalendar.getInstance(new Locale("es", "ES"));
    private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private Date fecha_inicial = null;
    private Date fecha_final = null;

    /**
     * Crea un objeto <code>Periodo</code> con fecha inicial y final iguales y se inicializan con la fecha y hora de creacion del objeto
     */
    public Periodo() {
        InicializarPeriodo();
    }

    /**
     * Crea un objeto <code>Periodo</code> a partir de las fechas dadas. Si la fecha es 'null' entonces la fecha se inicializa con la fecha y hora de
     * creacion del objeto
     * 
     * @param fecha_inicial
     * @param fecha_final
     */
    public Periodo(Date fecha_inicial, Date fecha_final) {
        InicializarPeriodo();
        if (fecha_inicial != null) setFecha_inicial(fecha_inicial);
        if (fecha_final != null) setFecha_final(fecha_final);
    }

    /**
     * Crea un objeto <code>Periodo</code> con: fecha inicial de primero de mes del año indicado, con la hora 01:00:00:00 fecha final de primero del
     * mes siguiente del año indicado, con la hora 00:00:00:00
     * 
     * @param mes
     * @param año
     */
    public Periodo(int mes, int año) {
        setFecha_inicial(calendario.getTime());
        setFecha_final(calendario.getTime());
        setFecha(FECHA_INICIAL, año, mes, 1, 1, 0, 0, 0);
        setFecha(FECHA_FINAL, año, mes + 1, 1, 0, 0, 0, 0);
    }

    /**
     * Crea un objeto <code>Periodo</code> con fecha inicial y final iguales a las del objeto <code>Periodo</code> pasado como parametro
     * 
     * @param periodo
     */
    public Periodo(Periodo periodo) {
        setFecha_inicial((Date) periodo.getFecha_inicial().clone());
        setFecha_final((Date) periodo.getFecha_final().clone());
    }

    /**
     * Si la medida es horaria, inicializa la fecha con la hora 01:00:00:00 Si la medida es cuartohorario, inicializa la fecha con la hora 00:15:00:00
     * 
     * @param fecha
     * @param medida
     */
    public void AjustarPeriodoCurvas(int medida) {
        calendario.setTime(fecha_inicial);
        if (medida == MEDIDA_TM1) {
            calendario.set(HORA, 1);
            calendario.set(MINUTO, 0);
        } else {
            calendario.set(HORA, 0);
            calendario.set(MINUTO, 15);
        }
        calendario.set(SEGUNDO, 0);
        calendario.set(MILISEGUNDO, 0);
        fecha_inicial.setTime(calendario.getTimeInMillis());
        calendario.setTime(fecha_final);
        calendario.add(DIA, 1);
        calendario.set(HORA, 0);
        calendario.set(MINUTO, 0);
        fecha_final.setTime(calendario.getTimeInMillis());
    }

    /**
     * Sólo compara la fecha, no la hora no segundos
     * 
     * @return Si la fecha inicial es menor devuelve -1, si es mayor devuelve 1 y si son iguales 0;
     */
    public int CompararFechas() {
        Date tmp_inicial = (Date) getFecha_inicial().clone();
        Date tmp_final = (Date) getFecha_final().clone();

        calendario.setTime(tmp_inicial);
        calendario.set(HORA, 0);
        calendario.set(MINUTO, 0);
        calendario.set(SEGUNDO, 0);
        calendario.set(MILISEGUNDO, 0);
        tmp_inicial = calendario.getTime();

        calendario.setTime(tmp_final);
        calendario.set(HORA, 0);
        calendario.set(MINUTO, 0);
        calendario.set(SEGUNDO, 0);
        calendario.set(MILISEGUNDO, 0);
        tmp_final = calendario.getTime();

        int valor = tmp_inicial.compareTo(tmp_final);
        if (valor > 0) return -1;
        return (valor == 0 ? 0 : 1);
    }

    /**
     * Compara 2 fecha teniendo en cuenta la hora, los minutos y los segundos.
     * 
     * @return Si la fecha inicial es menor devuelve -1, si es mayor devuelve 1 y si son iguales 0;
     */
    public int CompararFechasCompletas() {
        Date tmp_inicial = (Date) getFecha_inicial().clone();
        Date tmp_final = (Date) getFecha_final().clone();

        int valor = tmp_inicial.compareTo(tmp_final);
        if (valor > 0) return -1;
        return (valor == 0 ? 0 : 1);
    }

    /**
     * Asigna a la fecha inicial el 1 del mes actual, y la hora 01:00:00:00
     */
    public void DesdePrimeroMes() {
        setFecha(FECHA_INICIAL, getDatoFecha(FECHA_INICIAL, AÑO), getDatoFecha(FECHA_INICIAL, MES), 1, 1, 0, 0, 0);
    }

    /**
     * Asigna a la fecha inicial el 1 de enero del año que tenga ya la fecha inicial, y la hora 01:00:00:00
     */
    public void DesdePrimeroAño() {
        setFecha(FECHA_INICIAL, getDatoFecha(FECHA_INICIAL, AÑO), 0, 1, 1, 0, 0, 0);
    }

    /**
     * Asigna a la fecha inicial el 1 del mes solicitado y la hora 01:00:00:00, hasta el último día del mes y la hora 00:00:00:00
     */
    public void MesCompleto(int mes) {
        setFecha(FECHA_INICIAL, getDatoFecha(FECHA_INICIAL, AÑO), mes, 1, 1, 0, 0, 0);
        setFecha(FECHA_FINAL, getDatoFecha(FECHA_INICIAL, AÑO), mes, getDatoFecha(Periodo.FECHA_INICIAL, Periodo.DIAS_MES), 0, 0, 0, 0);
    }

    /**
     * 
     * @param fecha
     * @param dato
     *            Utilizar las constantes para : año,mes,dia,hora,minuto,segundo,milisegundo
     * @return El mes es 0:Enero, 1:Febrero, ....
     */
    public int getDatoFecha(int fecha, int dato) {
        if (fecha == FECHA_INICIAL) calendario.setTime(fecha_inicial);
        else calendario.setTime(fecha_final);
        if (dato == DIAS_MES) return calendario.getActualMaximum(DIA);
        if (dato == DIAS_AÑO) return calendario.getActualMaximum(Calendar.DAY_OF_YEAR);

        return calendario.get(dato);
    }

    /**
     * 
     * @param fecha
     * @param dato
     * @param valor
     *            Los meses comienzan en 0.
     */
    public void setDatoFecha(int fecha, int dato, int valor) {
        if (fecha == FECHA_INICIAL) calendario.setTime(fecha_inicial);
        else calendario.setTime(fecha_final);
        calendario.set(dato, valor);
        if (fecha == FECHA_INICIAL) setFecha_inicial(calendario.getTime());
        else setFecha_final(calendario.getTime());
    }

    public Date getFecha_final() {
        return fecha_final;
    }

    public Date getFecha_inicial() {
        return fecha_inicial;
    }

    /**
     * Devuelve un String de la fecha con el formato indicado
     * 
     * @param formato
     * @param fecha
     * @param separador_fecha
     *            Separador de fecha '-' o '/'. Se deben utilizar la constante : SEPARADOR_FECHA_xxxx
     * @return
     */
    public String getFechaFormateada(int formato, int fecha, int separador_fecha) {
        CambiarFormato(formato);
        String separador = separador_fecha == SEPARADOR_FECHA_GUION ? "-" : "/";
        if (fecha == FECHA_INICIAL) return sdf.format(getFecha_inicial()).replaceAll("-", separador);
        return sdf.format(getFecha_final()).replaceAll("-", separador);
    }

    /**
     * @param fecha
     * @return Regresa 'true' si la fecha esta dentro del periodo. Incluye ambos limites del periodo.
     */
    public boolean estaEnPeriodo(Date fecha) {
        if (getFecha_inicial().getTime() <= fecha.getTime() && getFecha_final().getTime() >= fecha.getTime()) return true;
        return false;
    }

    public void setFecha_final(Date fechaFinal) {
        fecha_final = fechaFinal;
    }

    public void setFecha_final(String fechaFinal, int formato) throws FormateoFechaException {
        fecha_final = ParsearFecha(fechaFinal, formato);
    }

    public void setFecha_inicial(Date fechaInicial) {
        fecha_inicial = fechaInicial;
    }

    public void setFecha_inicial(String fechaInicial, int formato) throws FormateoFechaException {
        fecha_inicial = ParsearFecha(fechaInicial, formato);
    }

    public void setPeriodoMes(int mes) {
        setFecha(FECHA_INICIAL, getDatoFecha(FECHA_INICIAL, AÑO), mes, 1, 1, 0, 0, 0);
        setFecha(FECHA_FINAL, getDatoFecha(FECHA_INICIAL, AÑO), mes + 1, 1, 0, 0, 0, 0);
    }

    private void CambiarFormato(int formato) {
        if (formato == FORMATO_FECHA_AAAA_MM_DD_HH_MM) sdf.applyLocalizedPattern("yyyy-MM-dd HH:mm:ss");
        if (formato == FORMATO_FECHA_DD_MM_AAAA_HH_MM) sdf.applyLocalizedPattern("dd-MM-yyyy HH:mm:ss");
        if (formato == FORMATO_FECHA_AAAA_MM_DD) sdf.applyLocalizedPattern("yyyy-MM-dd");
        if (formato == FORMATO_FECHA_DD_MM_AAAA) sdf.applyLocalizedPattern("dd-MM-yyyy");
    }

    /**
     * Siempre inicializa un objecto <code>Periodo</code> desde la primera hora de la mañana hasta la hora en que se creó el objecto. <br>
     * El día, por defecto, siempre comienza a las 01:00 y termina a las 00:00 del día siguiente. La fecha final siempre tiene la actual por defecto.
     */
    private void InicializarPeriodo() {
        fecha_inicial = calendario.getTime();
        fecha_final = calendario.getTime();
        setFecha(FECHA_INICIAL, getDatoFecha(FECHA_INICIAL, AÑO), getDatoFecha(FECHA_INICIAL, MES), getDatoFecha(FECHA_INICIAL, DIA), 1, 0, 0, 0);
    }

    private Date ParsearFecha(String fecha, int formato) throws FormateoFechaException {
        CambiarFormato(formato);
        try {
            return sdf.parse(fecha);
        } catch (ParseException e) {
            throw new FormateoFechaException(fecha.toString());
        }
    }

    /**
     * 
     * @param fecha
     * @param año
     * @param mes
     *            Se numeran desde 0-Enero a 11-Diciembre
     * @param dia
     * @param hora
     * @param minuto
     * @param segundo
     * @param milisegundo
     */
    private void setFecha(int fecha, int año, int mes, int dia, int hora, int minuto, int segundo, int milisegundo) {
        calendario.set(AÑO, año);
        calendario.set(MES, mes);
        calendario.set(DIA, dia);
        calendario.set(HORA, hora);
        calendario.set(MINUTO, minuto);
        calendario.set(SEGUNDO, segundo);
        calendario.set(MILISEGUNDO, milisegundo);
        if (fecha == FECHA_INICIAL) fecha_inicial.setTime(calendario.getTimeInMillis());
        else fecha_final.setTime(calendario.getTimeInMillis());
    }
}
